#pragma once

#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>

#define LOG_FILE_PATH "./Log/"
#define LOGFILE       "log.txt"
#define SIZE 1024

#define  _INFO_   1
#define  _DEBUG_  2
#define _WARNING_ 3
#define  _ERROR_  4
#define  _FATAL_  5

#define SCREEN    1
#define ONEFILE   2
#define CLASSFILE 3

class Log
{
public:
    Log()
    {
       _printmethod = SCREEN;
       _path = LOG_FILE_PATH;
    }

    void Enable(int method)
    {
        _printmethod = method;
    }

    std::string level_to_string(int level)
    {
        switch(level)
        {
            case _INFO_:
                return "INFO";
            case _DEBUG_:
                return "DEBUG";
            case _WARNING_:
                return "WARNING";
            case _ERROR_:
                return "ERROR";
            case _FATAL_:
                return "FATAL";
            default:
                return "NONE";
        }
    }

    void logprint(int level, const std::string& logtxt)
    {
        switch(_printmethod)
        {
            case SCREEN:
                std::cout << logtxt << std::endl;
                break;

            case ONEFILE:
                print_onefile(LOGFILE, logtxt);
                break;

            case CLASSFILE:
                print_classfile(level, logtxt);
                break;
        }
    }

    void print_onefile(const std::string& logname, const std::string& logtxt)
    {
        std::string _logname = _path + logname;
        int n = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND);
        if (n < 0)
        {
            return;
        }

        write(n, logtxt.c_str(), logtxt.size());
        close(n);
    }


    void print_classfile(int level, const std::string& logtxt)
    {
        std::string logname = LOGFILE;
        logname += ".";
        logname += level_to_string(level);
        print_onefile(logname, logtxt);
    }
    
    void operator()(int level, const char* format, ...)
    {
        char leftbuf[SIZE];
        time_t t = time(nullptr);
        struct tm* ct = localtime(&t);
        snprintf(leftbuf, sizeof(leftbuf), "[%s][%d-%d-%d %d:%d:%d]", level_to_string(level).c_str(),
                 ct->tm_year+1900, ct->tm_mon+1, ct->tm_mday, ct->tm_hour, ct->tm_min, ct->tm_sec);

        va_list v;
        va_start(v, format);
        char rightbuf[SIZE];
        vsnprintf(rightbuf, sizeof(rightbuf), format, v);
        va_end(v);

        char logtxt[SIZE*2];
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuf, rightbuf);

        logprint(level, logtxt);
    }

    ~Log()
    {}

private:
    int _printmethod;
    std::string _path;
};
